package top.hmtools.jsCss.common;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.BOMInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ResourceUtils;

import top.hmtools.base.CharsetGuessTools;

public class CommonTools {

    private static Logger logger = LoggerFactory.getLogger(CommonTools.class);

    // org.springframework.util.ResourceUtils 可用于操作已写URI、URL解析

    /**
     * 从指定资源读取指定后缀的内容到字典中。
     * 
     * @param resources
     *            资源集合
     * @param repertory
     *            字典
     * @param suffix
     *            后缀
     */
    public static void loadContent(String encoding,String rootPath, Enumeration<URL> resources,Map<String, ResourcesBean> repertory, String... suffixes) {
        if (resources == null || !resources.hasMoreElements()|| suffixes == null || suffixes.length < 1) {
            return;
        }
        List<ResourcesBean> resourcesBeans = new ArrayList<>();
        while(resources.hasMoreElements()){
            URL url = resources.nextElement();
            logger.info("当前扫描并加装静态资源文件的根路径是：{}",url.toString());
            boolean fileURL = ResourceUtils.isFileURL(url);
            boolean jarURL = ResourceUtils.isJarURL(url);
            if(fileURL){
                List<ResourcesBean> resourcesFilePath = getResourcesFilePath(encoding,rootPath,url, suffixes);
                resourcesBeans.addAll(resourcesFilePath);
            }else if(jarURL){
                try {
                    List<ResourcesBean> resourcesJarPath = getResourcesJarPath(encoding,rootPath,url, suffixes);
                    resourcesBeans.addAll(resourcesJarPath);
                } catch (IOException e) {
                    logger.error("从jar中读取静态资源文件异常："+e.getMessage(),e);
                }
            }
        }
        for(ResourcesBean rb:resourcesBeans){
            repertory.put(rb.getKeyName().trim().toLowerCase(), rb);
        }
    }

    /**
     * 猜测指定文件的字符编码
    * <br>方法说明：                    guessEncoding
    * <br>输入参数说明：           
    * <br>@param file
    * <br>@return
    * <br>@throws IOException
    * <br>输出参数说明：
    * <br>String           
    *
     */
    public static String guessEncoding(File file) throws IOException {
        return guessEncoding(FileUtils.readFileToByteArray(file));
    }

    /**
     * 猜测指定字节流的字符编码
    * <br>方法说明：                    guessEncoding
    * <br>输入参数说明：           
    * <br>@param bytes
    * <br>@return
    * <br>输出参数说明：
    * <br>String           
    *
     */
    public static String guessEncoding(byte[] bytes) {
        return CharsetGuessTools.doGuess(bytes);
    }

    /**
     * 获取依赖的所有jar包下classpath路径中指定后缀的静态资源文件
     * @param rootPath
     * @param url
     * @param suffixes
     * @return
     * @throws IOException
     */
    @SuppressWarnings("resource")
	public static List<ResourcesBean> getResourcesJarPath(String encodingParam,String rootPath,URL url,String... suffixes) throws IOException{
        List<ResourcesBean> result = new ArrayList<>();
        URLConnection con = url.openConnection();
        JarURLConnection jarCon = (JarURLConnection) con;
        JarFile jarFile = jarCon.getJarFile();
        Enumeration<JarEntry> entries = jarFile.entries();
        while(entries.hasMoreElements()){
            JarEntry jarEntry = entries.nextElement();
            //如果是文件夹，则直接跳过
            if(jarEntry.isDirectory()){
                continue;
            }
            String entryName = jarEntry.getName();//static/demoCss/gbkDemo.css
            
            for(String suffix:suffixes){
                if(entryName.toLowerCase().endsWith(suffix.toLowerCase())){
                    InputStream inputStream = jarFile.getInputStream(jarEntry);
                    // 猜测原始文件字符编码
                    byte[] byteArray = IOUtils.toByteArray(inputStream);
                    
                    String encoding = guessEncoding(byteArray);
                    encoding = encoding == null?encodingParam:encoding;////如果读取的文档内容为空，那么是猜测不出是何种字符编码，因此只能使用项目配置的字符编码 
                    String absolutePathStr = entryName;
                    String fileAbsolutelyPath = absolutePathStr;
                    String fileBaseName = FilenameUtils.getBaseName(absolutePathStr);
                    String fileContent = new String(byteArray,encoding);
                    //处理bom头
                    InputStream inputStreamToKillBom = IOUtils.toInputStream(fileContent, encoding);
                    BOMInputStream bomInputStream = new BOMInputStream(inputStreamToKillBom);
                    fileContent = IOUtils.toString(bomInputStream, encoding);
                    
                    String fileExtension = FilenameUtils.getExtension(absolutePathStr);
                    
                    String fileFullName = FilenameUtils.getName(absolutePathStr);
                    String fileRelativePath = absolutePathStr.replace(url.getFile(), "");
                    String fileAbsolutelyDir = FilenameUtils.getFullPath(absolutePathStr);
                    String fileRelativeDir = fileAbsolutelyDir.replace(url.getFile(), "");
                    
                    ResourcesBean rb = new ResourcesBean();
                    rb.setEncoding(encoding);
                    rb.setFileAbsolutelyPath(fileAbsolutelyPath);
                    rb.setFileBaseName(fileBaseName);
                    rb.setFileContent(fileContent);
                    rb.setFileExtension(fileExtension);
                    
                    rb.setFileFullName(fileFullName);
                    rb.setFileRelativePath(fileRelativePath);
                    rb.setFileAbsolutelyDir(fileAbsolutelyDir);
                    rb.setFileRelativeDir(fileRelativeDir);
                    rb.setRootPath("");
                    
                    result.add(rb);
                }
            }
        }
        return result;
    }

    /**
	 * 获取指定目录URL下多所有指定扩展名的资源文件 <br>
	 * @param rootPath
	 * @param url
	 * @param suffixes
	 * @return
	 */
	public static List<ResourcesBean> getResourcesFilePath(String encoding,String rootPath,URL url,String... suffixes) {
	    // url 转 file path
	    String dirStr = url.getFile();
	    File dir = new File(dirStr);
	    return getResourcesFilePath(encoding,rootPath,dir, suffixes);
	}

	/**
     * 获取指定目录file path下多所有指定扩展名的资源文件 <br>
     * @param rootPath
     * @param dir
     * @param suffixes
     * @return
     */
    public static List<ResourcesBean> getResourcesFilePath(String encodingParam,String rootPath,File dir,String... suffixes) {
        List<ResourcesBean> result = null;
        if (!dir.exists()) {
            return result;
        }
        // 获取路径下指定后缀名（扩展名）的所有文件，包括子目录。
        Collection<File> files = FileUtils.listFiles(dir, suffixes, true);
        result = new ArrayList<>();
        for (File file : files) {
            byte[] byteArray;
            try {
                byteArray = FileUtils.readFileToByteArray(file);
                String encoding = guessEncoding(byteArray);
                encoding = encoding==null?encodingParam:encoding;//如果读取的文档内容为空，那么是猜测不出是何种字符编码，因此只能使用项目配置的字符编码
                String absolutePathStr = file.getAbsolutePath();
                String fileAbsolutelyPath = absolutePathStr;
                String fileBaseName = FilenameUtils.getBaseName(absolutePathStr);
                String fileContent = new String(byteArray,encoding);
                //处理bom头
                InputStream inputStreamToKillBom = IOUtils.toInputStream(fileContent, encoding);
                BOMInputStream bomInputStream = new BOMInputStream(inputStreamToKillBom);
                fileContent = IOUtils.toString(bomInputStream, encoding);
                
                String fileExtension = FilenameUtils.getExtension(absolutePathStr);
                
                String fileFullName = FilenameUtils.getName(absolutePathStr);
                String fileRelativePath = absolutePathStr.replace(dir.getAbsolutePath(), "");
                String fileAbsolutelyDir = FilenameUtils.getFullPath(fileAbsolutelyPath);
                String fileRelativeDir = fileAbsolutelyDir.replace(dir.getAbsolutePath(), "");
                
                ResourcesBean rb = new ResourcesBean();
                rb.setEncoding(encoding);
                rb.setFileAbsolutelyPath(fileAbsolutelyPath);
                rb.setFileBaseName(fileBaseName);
                rb.setFileContent(fileContent);
                rb.setFileExtension(fileExtension);
                
                rb.setFileFullName(fileFullName);
                rb.setFileRelativePath(fileRelativePath);
                rb.setFileAbsolutelyDir(fileAbsolutelyDir);
                rb.setFileRelativeDir(fileRelativeDir);
                rb.setRootPath(rootPath);
                
                result.add(rb);
            } catch (IOException e) {
                logger.error("读取文件发送异常："+e.getMessage(),e);
            }
        }
        return result;
    }

    /**
     * 获取默认的类加载器 <br>
     * 参照spring扫描加载class中的源代码所编写。 <br>
     * 方法说明： getDefaultClassLoader <br>
     * 输入参数说明： <br>
     * @return <br>
     * 输出参数说明： <br>
     * ClassLoader
     *
     */
    public static ClassLoader getDefaultClassLoader() {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        } catch (Throwable ex) {
            // Cannot access thread context ClassLoader - falling back...
        }
        if (cl == null) {
            // No thread context class loader -> use class loader of this class.
            cl = CommonTools.class.getClassLoader();
            if (cl == null) {
                // getClassLoader() returning null indicates the bootstrap
                // ClassLoader
                try {
                    cl = ClassLoader.getSystemClassLoader();
                } catch (Throwable ex) {
                    // Cannot access system ClassLoader - oh well, maybe the
                    // caller can live with null...
                }
            }
        }
        return cl;
    }

    /**
     * 获取classpath中指定前缀路径下的所有路径URL集合，包括磁盘file路径、jar路径 <br>
     * 方法说明： getURLs <br>
     * 输入参数说明： <br>
     * @param path 路径前缀（必须在classpath下） <br>
     * @return <br>
     * @throws IOException <br>
     * 输出参数说明： <br>
     * Enumeration<URL>
     *
     */
    public static Enumeration<URL> getURLs(String path) throws IOException {
        ClassLoader cl = getDefaultClassLoader();
        return (cl != null
                ? cl.getResources(path)
                : ClassLoader.getSystemResources(path));
    }
}
